package dynamicrange;

import ikelib.Median;
import ikelib.drawTools;
import ikelib.menuUtil;
import ikelib.wintime;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeMap;

import javax.imageio.ImageIO;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;

import wcg.ChannelOutlineData;
import wcg.ChannelOutlineDataCollection;
import wcg.DialogTimeSetting;
import wcg.setStartTime;
import wcg.stateChangeNotify;
import wcg.winDataBase;
import wcg.winchkgProperty;


public class DynamicRangeView extends JPanel implements Printable,MouseListener,MouseMotionListener,
		setStartTime {
	
	static final int   LeftSize = 100;
	static final int   BottomSize = 100;
	static final int   TopMargin = 20;
	static final int   RightMargin = 20;
	
	
	public static final int FilterModeNone = 0x0000;
	public static final int FilterModeMedian3 = 0x0101;
	public static final int FilterModeMedian5 = 0x0102;
	public static final int FilterModeMedian11 = 0x0105;
	public static final int FilterModeMedian33 = 0x010B;
	static int[] 	FilterModeCode 		= { 0x0000, 0x0103, 0x0105, 0x010b, 0x011f };
	static String[] FilterModeString 	= { "None", "Median 3", "Median 5", "Median 11", "Median 31" };
	
	private int FilterMode = 0; // xx-yy xx:mode 0=none,1:median : yy:param 
	
	private boolean ShowInfo = true;
	private int ColorMode = 0;
	private wintime CurrentMouseTime = new wintime();
	private double  CurrentMouseAmp = 0.0;
	private Color ColorCursor = Color.red;  //  @jve:decl-index=0:
	private Point CurrentMousePositon = new Point();  //  @jve:decl-index=0:
	private int CursorDisplay = 0;
	private boolean     IsPrint = false;
	private Rectangle 	PlotArea = null;
	private Rectangle 	TimeAxisArea = null;
	private Rectangle 	AmpAxisArea = null;
	private Rectangle   MiscArea = null;
	private Rectangle   Legend = null;
	
	private wintime  DispStartTime = new wintime();  // #0 @jve:decl-index=0:
	private int DispTimeMin = 60;
	private Rectangle PreRect = new Rectangle();
	private double YDispMax = 1000.0;
	private double YDispMin = -1000.0;
	private winDataBase DataBase = null;
	//private int FullScale = 8388607;
	
	private double YDataMax = 1000.0;
	private double YDataMin = -1000.0;
	private boolean DirtyFlag = true;
	private BasicStroke Dash=null;  //  @jve:decl-index=0:
	private BasicStroke BoldStroke = null; 
	private TreeMap<Integer,PlotData> PltData= new TreeMap<Integer,PlotData>();
	class PlotData{
		public int		FullScale=8388607;
		public boolean disp = false;
		public Color color = Color.black;
		public ArrayList<Integer> x = new ArrayList<Integer>();
		public ArrayList<Integer> y = new ArrayList<Integer>();
		public double Max = 0.0;
		public double Min = 0.0;
		public double Mean = 0.0;
	}
	
	private stateChangeNotify Parent = null;


	public DynamicRangeView(stateChangeNotify mama){
		this.setPreferredSize(new Dimension(300,300));
		AmpAxisArea  = new Rectangle(0,TopMargin,LeftSize,100);
		PlotArea 	 = new Rectangle(LeftSize,TopMargin,100,100);
		Legend       = new Rectangle(LeftSize+20,0+20,50,20);
		TimeAxisArea = new Rectangle(LeftSize,100,100,100);
		MiscArea = new Rectangle(0,100,LeftSize,BottomSize);
		addMouseListener(this);
		addMouseMotionListener(this);
		mYSelf = this;
		Parent = mama;
	}

	
	//----------------------------------------------
	// 表示開始時刻
	public wintime getDispStartTime() {
		return DispStartTime;
	}

	public void setDispStartTime(wintime dispStartTime) {
		if(DispStartTime == dispStartTime) return;
		DispStartTime = dispStartTime;
		DirtyFlag = true;
	}

	//----------------------------------------------
	// 表示時間
	public int getDispTimeMin() {
		return DispTimeMin;
	}

	public void setDispTimeMin(int dispTimeMin) {
		if(DispTimeMin==dispTimeMin) return;
		if(dispTimeMin<3) dispTimeMin=2;
		DispTimeMin = dispTimeMin;
		DirtyFlag = true;
	}

	//----------------------------------------------
	// 表示最大値
	public double getYDispMax() {
		return YDispMax;
	}

	public void setYDispMax(double yDispMax) {
		if(YDispMax == yDispMax) return ;
		YDispMax = yDispMax;
		DirtyFlag = true;
	}

	//----------------------------------------------
	// 表示最小値
	public double getYDispMin() {
		return YDispMin;
	}

	public void setYDispMin(double yDispMin) {
		if(YDispMin==yDispMin) return ;
		YDispMin = yDispMin;
		DirtyFlag = true;
	}
	
	
	
	private static Color getColor(int ch){
		int col = ch ^ (ch>>8) ^ (ch>>16) ^ (ch>>24);
		if(col<0) col*=-1;
		col = col & 0xff;
		int col2=0;
		if((col&0x01)!=0) col2 |= 0x80;
		if((col&0x02)!=0) col2 |= 0x40;
		if((col&0x04)!=0) col2 |= 0x20;
		if((col&0x08)!=0) col2 |= 0x10;
		if((col&0x10)!=0) col2 |= 0x08;
		if((col&0x20)!=0) col2 |= 0x04;
		if((col&0x40)!=0) col2 |= 0x02;
		if((col&0x80)!=0) col2 |= 0x01;
				//int col2 = ((col >> 4)&0x0f) | ((col << 4)&0xf0);
		float hue = (float)col2/0xff;
		return Color.getHSBColor(hue, 0.8f, 0.8f);
	}
	private Object DataBaseLock(){
		return DataBase.Lock;
	}
	//-----------------------------------------------------------------------------------------------------
	
	// -------------------------
	// png にエクスポート
	public String ExportImageDir = null;
	
	public boolean exportAsImage(String dir,double ratex,double ratey){
		Dimension dm = this.getSize(); 

		BufferedImage img = new BufferedImage((int)(dm.width*ratex), (int)(dm.height*ratey), BufferedImage.TYPE_INT_RGB);
		Graphics2D g = img.createGraphics();
		g.scale(ratex,ratey);
		IsPrint=true;
		paintComponent(g);
		IsPrint=false;
		
        // ファイルネームを作る
		String chname = new String();
		Set<Integer> ks = PltData.keySet();
		Iterator<Integer> ite = ks.iterator();
		while(ite.hasNext()){
			int ch = ite.next();
			PlotData pd = PltData.get(ch);
			if(pd.disp){
				chname += String.format("%04x", ch);
			}
			
		}
		if(chname.isEmpty()) return false;
		wintime endtm = new wintime(DispStartTime);
		endtm.inc_sec(DispTimeMin*60);
		String filename = new String();
		filename = dir + "DR-"+DispStartTime.fmt2()+"-"+endtm.fmt2()+".png";
		
		//------------------------------------------------------
		// save as ダイアログ
		JFileChooser filechooser = new JFileChooser(dir);
		filechooser.setFileSelectionMode( JFileChooser.FILES_ONLY);	      
		FileNameExtensionFilter filter = new FileNameExtensionFilter("png file", "png");
		filechooser.addChoosableFileFilter(filter);
		filechooser.setDialogTitle("Save as");
		File tmp = new File(filename);
		filechooser.setSelectedFile(tmp);
		//filechooser.ensureFileIsVisible(tmp);
		int selected = filechooser.showSaveDialog(null);
		if ( selected == JFileChooser.APPROVE_OPTION ) {
			tmp = filechooser.getSelectedFile();
			if(tmp.exists()){
				if(JOptionPane.NO_OPTION ==  JOptionPane.showConfirmDialog(null, "Over write ?","Confirmation",JOptionPane.YES_NO_OPTION)){
					return false;
				}
			}
			filename = tmp.getAbsolutePath();
			ExportImageDir = new String(tmp.getParentFile().getAbsolutePath()+File.separator);
		}
		else{
			return false;
		}
		//------------------------------------------------------
		try {
			ImageIO.write(img, "png", new File(filename));
		} catch (IOException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, "File Output Error", "Error", JOptionPane.ERROR_MESSAGE); 
			return false;
		}
		return true;
	}

	//-----------------------------------------------------------------------------------------------------
	public int print(Graphics g, PageFormat pf, int arg2)
			throws PrinterException {
		if(arg2 >= 1) return Printable.NO_SUCH_PAGE;
		
		Graphics2D g2 = (Graphics2D)g;
		g2.translate(pf.getImageableX(), pf.getImageableY());
		g2.scale(0.75,0.75);
		double hight = pf.getImageableHeight();
		double width = pf.getImageableWidth();
		Dimension dm = new Dimension();
		dm.setSize(width/0.75,hight/0.75);
		
		//CurrentDPI=72; 
		IsPrint=true;
		paintComponetSub(g2,dm);
		//CurrentDPI=96;
		IsPrint=false;
		return 0;
	}
	
	
	// ------------------------------
	// すべてを書き直し
	public void repaintAll(){
		Rectangle r = new Rectangle(0,0,getWidth(),getHeight());
		repaint(r);
	}

	@Override
	public void paintComponent(Graphics g){
		Dimension dm = this.getSize();
		paintComponetSub((Graphics2D)g,dm);
	}
	//-------------------------------------------------------
	private void paintComponetSub(Graphics2D g,Dimension dm){
		g.setBackground(Color.WHITE);
		g.setColor(Color.BLACK);
		g.clearRect(0,0,dm.width,dm.height);
		AmpAxisArea.height 	= dm.height - BottomSize - TopMargin;
		AmpAxisArea.y 		= TopMargin;
		PlotArea.width = dm.width - LeftSize - RightMargin;
		PlotArea.height = dm.height - BottomSize - TopMargin;
		PlotArea.y 		= TopMargin;
		TimeAxisArea.y = dm.height - BottomSize;
		TimeAxisArea.width = dm.width - LeftSize - RightMargin;
		
		MiscArea.y = dm.height - BottomSize;
		paintTimeAxis(g,TimeAxisArea);
		paintAmpAxis(g,AmpAxisArea);
		drawDraggMarker((Graphics2D)g,PlotArea);
		paintPlot((Graphics2D)g,PlotArea);
		paintCursor((Graphics2D)g,PlotArea);
		
	}
	//---------------------------------------------------------------------------
	public void setDataBase(winDataBase db){
		DataBase=db;
		DirtyFlag=true;
	}
	//---------------------------------------------------------------------------
	public void setTimeLatest(){
		wintime t = new wintime(DataBase.LatestTime);
		DispStartTime = new wintime(t.year(),t.month(),t.day(),t.hour(),t.min(),0,0);
//		for(int i=0;i<DispTimeMin;i++){
//			DispStartTime.dec_min();
//		}
		DispStartTime.dec_sec(DispTimeMin*60);
		DirtyFlag=true;
	}
	//---------------------------------------------------------------------------
	public void showChannel(int ch,boolean show){
		ChannelOutlineDataCollection col = DataBase.getChannelDataCollection60(ch);
		if(col==null) return;
		PlotData pd = PltData.get(ch);
		if(pd==null) {
			pd = new PlotData();
			pd.color = getColor(ch);
			PltData.put(ch,pd);
		}
		pd.disp = show;
		DirtyFlag=true;
	}
	//---------------------------------------------------------------------------
	/*
	 * 自動スケール　（振幅、時間）
	 */
	public void autoScale(){
		setAutoScaleTime();
		setData();
		setAutoScaleAmplitude();
		DirtyFlag=true;
		repaintAll();
	}
	
	/*
	 *  振幅軸のスケールをフィットさせる
	 */
	private void setAutoScaleAmplitude() {
		double	yspan  = (double)((int)(YDataMax - YDataMin + 2.0));
		double ycenter = (int)(YDataMax+YDataMin)/2;
		YDispMax = ycenter + yspan/2 ;
		YDispMin = ycenter - yspan/2 ;
	}
	//---------------------------------------------------------------------------
	private double getSNR(ChannelOutlineData dat,int fullscale){
		double d = dat.getValiance();
		d = Math.sqrt(d);
		d = 20*Math.log10(d/(double)fullscale);
		return d;
	}
	
	//---------------------------------------------------------------------------
	private void clearDispData(){
		Set<Integer> s = PltData.keySet();
		Iterator<Integer> ite = s.iterator();
		while(ite.hasNext()){
			int ch = ite.next();
			PlotData pd = PltData.get(ch);
			if(pd!=null){
				pd.x.clear();
				pd.y.clear();
			}
		}
	}
	//---------------------------------------------------------------------------
	public void setData(){
		
		double ymax = -Double.MAX_VALUE;
		double ymin = Double.MAX_VALUE;
		
		clearDispData();
		Iterator<Integer> itech = DataBase.iteratorChannel();
		double fullscale = YDispMax-YDispMin;
		while(itech.hasNext()){
			int ch = itech.next();
			ChannelOutlineDataCollection col = DataBase.getChannelDataCollection60(ch);
			if(col==null) continue;
			PlotData pd = PltData.get(ch);
			if(pd==null){
				pd = new PlotData();
			}
			pd.x.clear();
			pd.y.clear();
			pd.FullScale = DataBase.getFullScale(ch);
			if(pd.disp==false) continue;
			wintime tm = new wintime(DispStartTime);
			int y1=0;
			int x1=0; 
			double sum=0.0;
			double ychmax = -1e300;
			double ychmin = 1e300;
			int count=0;
//			for(int i=0;i<DispTimeMin;i++,tm.inc_min()){
//				ChannelOutlineData dat = col.getChannelData(tm);
//				if(dat==null) continue;
//				x1 = (int)(((double)PlotArea.width-1)*i/DispTimeMin);
//				double d = getSNR(dat,pd.FullScale);
//				sum += d;
//				count++;
//				if(d>ymax) ymax=d;
//				if(d<ymin) ymin=d;
//				if(d>ychmax) ychmax=d;
//				if(d<ychmin) ychmin=d;
//				y1 = (int)(((double)PlotArea.height-1)*(d - YDispMin)/fullscale);
//				pd.x.add(x1);
//				pd.y.add(y1);
//			}
///////////////////////////
			LinkedList<Double> datalist=new LinkedList<Double>();
			LinkedList<Integer> x1list=new LinkedList<Integer>();
			for(int i=0;i<DispTimeMin;i++,tm.inc_min()){
				ChannelOutlineData dat = col.getChannelData(tm);
				if(dat==null) continue;
				x1 = (int)(((double)PlotArea.width-1)*i/DispTimeMin);
				x1list.add(x1);
				double d = getSNR(dat,pd.FullScale);
				datalist.add(d);
				count++;
			}
			//
			// フィルターモードを選択する
			//
			switch((FilterMode>>8)&0xff){
			case 1:	// median mode;
				int width = FilterMode & 0xff;
				Median med = new Median(width);	// wはダミー
				datalist = med.calc(datalist); // メジアンフィルタをかける
				break;
			}
			
			Iterator<Double> itedouble = datalist.iterator();
			Iterator<Integer> iteint = x1list.iterator();
			while(itedouble.hasNext()){
				double d = itedouble.next();
				sum += d;
				if(d>ymax) ymax=d;
				if(d<ymin) ymin=d;
				if(d>ychmax) ychmax=d;
				if(d<ychmin) ychmin=d;
				y1 = (int)(((double)PlotArea.height-1)*(d - YDispMin)/fullscale);
				int x11 = iteint.next();
				pd.x.add(x11);
				pd.y.add(y1);
			}
///////////////////////
			pd.Max = ychmax;
			pd.Min = ychmin;
			pd.Mean = sum/count;
			PltData.put(ch,pd);
		}
		if(ymax != -Double.MAX_VALUE){
			YDataMax=ymax;
			YDataMin=ymin;
		}
		else{
			YDataMax = 100;
			YDataMin = -100;
		}
	}
	//---------------------------------------------------------------------------
	private int HightLightID = -1;
	private void paintPlot(Graphics2D g2,Rectangle area){
		// 原点を左下にして、上方向を＋にする
		Graphics2D g = (Graphics2D)g2.create();
		g.setClip(area.x-1,area.y-1,area.x+area.width+2,area.y+area.height+2);
		g.translate(area.x, area.y+area.height);
		g.scale(1.0,-1.0);
		///////////////////////////////////////
		g.drawRect(0,0,area.width,area.height);

	    Stroke bsorg = g.getStroke();
		if(Dash==null){
			float[] dasharray = {3.0f,3.0f};
	    	Dash = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER, 10.0f, dasharray, 0.0f);
		}
	    g.setStroke(Dash); 
		
		g.setColor(Color.gray);
		int div = 5;
		int max = area.width;
		int x1=0;
		int x2=0;
		int y1=0;
		int y2=area.height;
		for(int i=0;i<div+1;i++){
			x2=x1 = i*max/div;
			g.drawLine(x1, y1, x2, y2);
		}
		div = 10;
		max = area.height;
		x1=0;
		x2=area.width;
		y1=0;
		y2=0;
		for(int i=0;i<div+1;i++){
			y1=y2 = i*max/div;
			g.drawLine(x1, y1, x2, y2);
		}
		g.setColor(Color.black);
		g.setStroke(bsorg);
		///////////////////////////////////////
		if(!area.equals(PreRect)) DirtyFlag=true;
		if(DirtyFlag) setData();
		Set<Integer> ks = PltData.keySet();
		Iterator<Integer> ite = ks.iterator();
		LinkedList<Color> collist = null;
		LinkedList<String> strlist = null;
		LinkedList<Integer> idlist = null;
		if(ShowInfo){
			collist = new LinkedList<Color>();
			strlist = new LinkedList<String>();
			idlist  = new LinkedList<Integer>();
		}
		while(ite.hasNext()){
			int ch = ite.next();
			PlotData pd = PltData.get(ch);
			if(pd.disp==false) continue;
			g.setColor(pd.color);
			if(ShowInfo){
				collist.add(pd.color);
			}
			int [] ax = new int[pd.x.size()];
			int [] ay = new int[pd.x.size()];
			for(int i=0;i<pd.x.size();i++){
				ax[i] = pd.x.get(i);
				ay[i] = pd.y.get(i);
			}
			if(HightLightID==ch){
				Stroke bsorg2 = g.getStroke();	
				if(BoldStroke==null){
					BoldStroke = new BasicStroke(4.0f, BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER, 10.0f, null, 0.0f);
				}
				g.setStroke(BoldStroke);
				g.drawPolyline(ax,ay,ax.length);
				g.setStroke(bsorg2);
			}
			else{
				g.drawPolyline(ax,ay,ax.length);
			}
			if(ShowInfo){
				String mes = String.format("[%04x]", ch);
				if(pd.x.size()>0){
					mes += String.format("max=%6.2f min=%6.2f mean=%6.2f", pd.Max,pd.Min,pd.Mean);
				}
				strlist.add(mes);
				idlist.add(ch);
			}
		}
		///////////////////////////////////////
		
		///////////////////////////////////////
		g.setColor(Color.black);
		g.scale(1.0,-1.0);
		g.translate(-area.x, -area.y-area.height);
		DirtyFlag=false;
		if(ShowInfo){
			int legendx = 150;
			int legendy = 50;
			Iterator<Color> itecol = collist.iterator();
			Iterator<String> itestr = strlist.iterator();
			Iterator<Integer> iteid = idlist.iterator();
			HightLightID=-1;
			while(itecol.hasNext()){
				int id = iteid.next();
				Color col = itecol.next();
				String str = itestr.next();
				Rectangle r= new Rectangle(legendx,legendy-3,20,3*6);
				if(r.contains(CurrentMousePositon)){
					HightLightID=id; // ハイライトするＣＨ ＩＤ
				}
				g.setColor(col);
				g.drawLine(legendx,legendy,legendx+20,legendy);
				if(HightLightID==id) 	g.fillRect(legendx, legendy-1, 20, 3+2);
				else 				g.fillRect(legendx, legendy, 20, 3);
				drawTools.drawText(g,str,legendx+22,legendy,drawTools.POSITION_LEFT_CENTER|drawTools.FILL_RECT);
				
				legendy += 20;
			}
			g.setColor(Color.black);
		}
		else{
			HightLightID=-1;
		}
		g.setClip(0,0,0,0);
	}
	
	// ------------------------------------------
	// ドラッグマーカーを描く
	private void drawDraggMarker(Graphics2D gd,Rectangle area){
		if(DraggingState != 1) return;
		if(DraggWhat != 0) return; // プロットエリアのドラッグでない
		Graphics2D gd2 = (Graphics2D)gd.create(area.x,area.y,area.x+area.width,area.y+area.height);
		//gd2.clearRect(x1,y1,x2-x1,y2-y1);
		double xmax = area.width;
		double ymax = area.height;
		gd2.clipRect(0,0,(int)xmax,(int)ymax);
		
		gd2.setColor(new Color(100,100,100,50));
		int xstart ,width;
		
		if(PointDraggStart.x > PointDraggCur.x){ 	width = PointDraggStart.x - PointDraggCur.x ; xstart = PointDraggCur.x;}
		else						 {	width = PointDraggCur.x - PointDraggStart.x ; xstart = PointDraggStart.x; }
		gd2.fillRect(xstart-area.x,0,width,(int)ymax);
	}

	
	
	//---------------------------------------------------------------------------
	private void paintTimeAxis(Graphics g,Rectangle area){
		g.translate(area.x, area.y);
		//g.drawRect(0,0,area.width-1,area.height-1);
		int max = area.width;
		int div = 5;
		int x1;
		int y1 = 0;
		int y2 = 4;
		wintime tm = new wintime(DispStartTime);
		for(int i=0;i<div+1;i++){
			x1 = i*max/div;
			g.drawLine(x1, y1, x1, y2);
			int ypos = drawTools.POSITION_LEFT_TOP;
			if(i==div) ypos = drawTools.POSITION_RIGHT_TOP;
			drawTools.drawText((Graphics2D) g,tm.fmtSec(),x1,y1,ypos);
			drawTools.drawText((Graphics2D) g,tm.fmtDay(),x1,y1+12,ypos);
			tm.inc_sec(60*DispTimeMin/div);
		}
		
		
		g.translate(-area.x, -area.y);
	}
	//---------------------------------------------------------------------------
	private void paintAmpAxis(Graphics2D g,Rectangle area){
		g.translate(area.x, area.y);
	
		int max = area.height;
		int div = 10;
		int y1;
		int x1 = area.width-10;
		int x2 = area.width-1;
		double ydatarange = YDispMax -YDispMin; 
		for(int i=0;i<div+1;i++){
			y1 = i*max/div;
			g.drawLine(x1, y1, x2, y1);
			String label = String.format("%6.2f",YDispMax-i*ydatarange/10);
			int ypos = drawTools.POSITION_RIGHT_CENTER;
			if(i==0) ypos = drawTools.POSITION_RIGHT_TOP;
			else if(i==div) ypos = drawTools.POSITION_RIGHT_BOTTOM;
			drawTools.drawText((Graphics2D) g,label,x1,y1,ypos);
		}
		String title = new String("DynamicRange [dB]");
		drawTools.drawText((Graphics2D) g,title,area.x+5,area.y+area.height/2,drawTools.POSITION_CENTER_TOP,Math.PI*3.0/2.0);
			
		g.translate(-area.x, -area.y);
	}
	// ------------------------------------------
	// グラフエリアのx座標オフセットから時刻を計算する
	private wintime x2time(int xoffset,int xmax,wintime starttime,int timespanms){
		double tmoffset = ((double)xoffset)/(xmax); // -4 space)
		int tmoffsetint = (int)(tmoffset * (double)(timespanms)+0.5);
		wintime ret = new wintime(starttime);
		ret.inc_sec(tmoffsetint*60);
		return ret;
	}
	// ------------------------------------------
	// カーソルを描画する
	private void paintCursor(Graphics2D gd2,Rectangle r){
		if(IsPrint || CursorDisplay == 0) return;
		//Graphics2D gd2 = (Graphics2D)gd.create(x1,y1,x2,y2);
		//gd2.clearRect(x1,y1,x2-x1,y2-y1);
		double xmax = r.width;
		double ymax = r.height;
		
		//gd2.clipRect(0,0,(int)xmax,(int)ymax);
		gd2.clipRect(r.x,r.y,r.width,r.height);
		gd2.translate(r.x,r.y);
		gd2.setColor(ColorCursor);
		int cpx = CurrentMousePositon.x-r.x;
		int cpy = CurrentMousePositon.y-r.y;
		gd2.drawLine(cpx,0,cpx,(int)ymax);
		gd2.drawLine(0,cpy,(int)xmax,cpy);
		
		// カーソル位置から時間を計算
		CurrentMouseTime = x2time(cpx,r.width,DispStartTime,DispTimeMin);
		CurrentMouseAmp = YDispMax - (YDispMax - YDispMin)*cpy/r.height ;
		String xmes = String.format("%6.2f", CurrentMouseAmp,CurrentMouseTime.fmtSec());
		if(cpx < r.width/2) {
			if(cpy<20){
				drawTools.drawText(gd2, xmes, cpx+20, cpy, drawTools.POSITION_LEFT_TOP);
				drawTools.drawText(gd2, CurrentMouseTime.fmtSec(), cpx+2, r.height-2, drawTools.POSITION_LEFT_BOTTOM);
			}
			else{
				drawTools.drawText(gd2, xmes, cpx+20, cpy, drawTools.POSITION_LEFT_BOTTOM);
				drawTools.drawText(gd2, CurrentMouseTime.fmtSec(), cpx+2, r.height-2, drawTools.POSITION_LEFT_BOTTOM);
			}
		}
		else {
			if(cpy<20){
				drawTools.drawText(gd2, xmes, cpx-20, cpy, drawTools.POSITION_LEFT_TOP);
				drawTools.drawText(gd2, CurrentMouseTime.fmtSec(), cpx-2, r.height-2, drawTools.POSITION_RIGHT_BOTTOM);
			}
			else{
				drawTools.drawText(gd2, xmes, cpx-50, cpy, drawTools.POSITION_RIGHT_BOTTOM);
				drawTools.drawText(gd2, CurrentMouseTime.fmtSec(), cpx-2, r.height-2, drawTools.POSITION_RIGHT_BOTTOM);
			}
		}
		gd2.setClip(null);
		gd2.translate(-r.x,-r.y);
	}
	

	@Override
	public void mouseClicked(MouseEvent e) {
		int code = e.getButton();
		int mod  = e.getModifiers();
		
		Point cp = e.getPoint();
		if(Legend.contains(cp)){
		}
		else if(PlotArea.contains(cp)){
			DraggWhat = 0;
			mouseClickedPlotArea(e);
		}
		else if(TimeAxisArea.contains(cp)){
			DraggWhat = 1;
			mouseClickedTimeAxisArea(e);
		}
		else if(AmpAxisArea.contains(cp)){
			
		}
		else if(MiscArea.contains(cp)){
			mouseClickedMiscArea(e);
		}
		
	}
	/*
	 * メニューを作成する
	 */
	public void addMenuDisplaySelection(JPopupMenu parent){
		// チェックボックス付きのメニュー
		menuUtil.addCheckBoxMenuItem(parent, "Show Channel Information", 
				new ActionListener(){ 
					@Override
					public void actionPerformed(ActionEvent arg0) {
						if(ShowInfo) ShowInfo = false;
						else		 ShowInfo = true;
						repaintAll();
					};
				}
			).setSelected(ShowInfo);
		parent.addSeparator();
		//
		// Filterの下にカスケードしてラジオボタングループにする
		//
		JMenu filter = menuUtil.addMenu(parent,"Filter");
		ButtonGroup bg = new ButtonGroup();
		for(int i=0;i<FilterModeCode.length;i++){
			JRadioButtonMenuItem flt = menuUtil.addRadioButtonMenuItem(filter, FilterModeString[i], 
					new ActionListener(){ 
						@Override
						public void actionPerformed(ActionEvent arg0) {
							setFilterModeFromString(arg0.getActionCommand());
							//setFilterModeIndex(i);
						};
					}
				);
			bg.add(flt);
			if(getFilterModeIndex()==i){
				flt.setSelected(true);
			}
		}
		
	}
	
	private void mouseClickedMiscArea(MouseEvent e){
		if(SwingUtilities.isRightMouseButton(e)){// LeftButton Zoom out
			JPopupMenu PopupMenuMisc = new JPopupMenu();
			addMenuDisplaySelection(PopupMenuMisc);
			PopupMenuMisc.show(e.getComponent(), e.getX(), e.getY());
		}
		else if(SwingUtilities.isLeftMouseButton(e)){// RightButton Zoom in
		}
	}
	private void mouseClickedPlotArea(MouseEvent e) {
		int code = e.getButton();
		Point cp = e.getPoint();
		float xpos  = (cp.x - PlotArea.x)/(float)PlotArea.width;
		if(SwingUtilities.isRightMouseButton(e)){// LeftButton Zoom out 
			setTimeScaleZoom(2,1,xpos);
			repaintAll();

		}
		else if(SwingUtilities.isLeftMouseButton(e)){// RightButton Zoom in
			setTimeScaleZoom(1,2,xpos);	
			repaintAll();

		}
	}
	private void mouseClickedTimeAxisArea(MouseEvent e) {
		int mod  = e.getModifiers();
		if(SwingUtilities.isRightMouseButton(e)){
			if(0 != (mod & MouseEvent.CTRL_MASK)){
				// フィルタモードを巡回的に選択する
				int idx = getFilterModeIndex();
				if(++idx>=FilterModeCode.length) idx=0;
				setFilterModeIndex(idx);
				repaintAll();
			}
		}
	}
	@Override
	public void mouseEntered(MouseEvent e) {
		
	}

	@Override
	public void mouseExited(MouseEvent e) {
		CursorDisplay = 0;	
	}

	@Override
	public void mousePressed(MouseEvent e) {
		int code = e.getButton();
		int mod  = e.getModifiers();
		
		Point cp = e.getPoint();
		DraggWhat = -1;
		if(Legend.contains(cp)){
		}
		else if(PlotArea.contains(cp)){
			DraggWhat = 0;
		}
		else if(TimeAxisArea.contains(cp)){
			DraggWhat = 1;
		}
		else if(AmpAxisArea.contains(cp)){
		}
		else if(MiscArea.contains(cp)){
		}
	}
	private int DraggingState = 0;
	private Point PointDraggStart = new Point(0,0);  //  @jve:decl-index=0:
	//private Point PointDraggEnd= new Point(0,0);  //  @jve:decl-index=0:
	private Point PointDraggCur = new Point(0,0);  //  @jve:decl-index=0:
	private wintime DraggStartTime = new wintime();  //  @jve:decl-index=0:
	private wintime DraggEndTime = new wintime();  //  @jve:decl-index=0:
	private int DraggWhat = -1;	// 0=Zoom 1=TimeShift
	@Override
	public void mouseReleased(MouseEvent e) {
		if(DraggingState == 1){
			// ドラッグ終了時
			DraggingState = 0; 
			if(DraggWhat == 0){
				DraggEndTime = new wintime(CurrentMouseTime);
				wintime wtstart;
				int span;
				if(DraggEndTime.getValue() > DraggStartTime.getValue() ){
					wtstart = DraggStartTime;
					long tmd = DraggEndTime.getValue() - DraggStartTime.getValue();
					tmd /= 60000;
					span = (int)tmd;
				}
				else{
					wtstart = DraggEndTime;
					long tmd = DraggStartTime.getValue() - DraggEndTime.getValue();
					tmd /= 60000;
					span = (int)tmd;
				}
				if(span<3) span = 2;
				setDispStartTime(wtstart);
				setDispTimeMin(span);
				repaintAll();
			}
		}
	}
	private float DraggTimeShiftFactor = 0.0f;
	@Override
	public void mouseDragged(MouseEvent e) {
		if(DraggingState==0){
			DraggingState = 1; // ドラッグ開始
			PointDraggStart = e.getPoint();
			if(DraggWhat==0){
				DraggStartTime = new wintime(CurrentMouseTime);
			}
			else if(DraggWhat==1){
				DraggStartTime = new wintime(getDispStartTime());
				int     span  = getDispTimeMin();
				int dispwidth = TimeAxisArea.width;
				DraggTimeShiftFactor = (float)span/dispwidth;
			}
		}
		else if(DraggingState==1){
			// ドラッグ中
			PointDraggCur = e.getPoint();
			if(DraggWhat==0) {
				mouseMoved(e);
			}
			else if(DraggWhat==1){
				int delta = PointDraggStart.x -PointDraggCur.x;
				float shifttime = (DraggTimeShiftFactor * delta);
				wintime tm = new wintime(DraggStartTime);
				if(shifttime>=0) tm.inc_sec((int)(shifttime)*60);
				else             tm.dec_sec(-(int)(shifttime)*60);
				setDispStartTime(tm);
				DirtyFlag=true;
				repaintAll();
				onParameterChange();
				
			}
		}
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		if(PlotArea.contains(e.getX(),e.getY())){
			CursorDisplay = 1;
			if(!CurrentMousePositon.equals(e)){
				CurrentMousePositon.setLocation(e.getX(),e.getY());
				repaintAll();
			}
		}
		else{
			if(CursorDisplay != 0) {
				CursorDisplay = 0;
				repaintAll();
			}
		}
	}

	public void LoadSaveProperty(boolean save) {
		if(save){
			winchkgProperty.SNRColor = getColorMode();
			winchkgProperty.DynamicRangeExportImageDir = ExportImageDir;
			
		}
		else{
			setColorMode(winchkgProperty.SNRColor);
			ExportImageDir = winchkgProperty.DynamicRangeExportImageDir;
		}

		
	}
	// 今のところカラーモードを指定できない
	private void setColorMode(int sNRColor) {
		ColorMode = sNRColor;
		
	}

	private int getColorMode() {
		return ColorMode;
	}
	// ------------------------
	// フィルタの選択用
	// 
	private int getFilterMode() {
		return FilterMode;
	}
	private void setFilterMode(int filterMode) {
		if(FilterMode != filterMode){
			FilterMode = filterMode;
			onParameterChange();
			repaintAll();
		}
	}
	public String[] getFilterModeString(){
		return FilterModeString;
	}
	public void setFilterModeFromString(String mode){
		for(int i=0;i<FilterModeCode.length;i++){
			if(mode.compareTo(FilterModeString[i])==0){
				setFilterModeIndex( i);
			}
		}
	}
	public int getFilterModeIndex(){
		for(int i=0;i<FilterModeCode.length;i++){
			if(FilterModeCode[i]==FilterMode) return i;
		}
		return 0;
	}
	public void setFilterModeIndex(int idx){
		if(idx>=FilterModeCode.length) idx = 0;
		int code = FilterModeCode[idx];
		setFilterMode(code);
	}
	
	// ------------------------
	/**
	 * 時刻セットダイアログ
	 */
	public void doTimeSetDialog(){
		getDialogTimeSetting().setTime(DispStartTime);
		getDialogTimeSetting().setLocation(this.getLocationOnScreen());
		getDialogTimeSetting().setVisible(true);
	}
	private DialogTimeSetting DialogTmSet = null;	// 	時刻の直接設定
	private DialogTimeSetting getDialogTimeSetting(){
		if(DialogTmSet==null){
			DialogTmSet = new DialogTimeSetting(getParentFrame());
			DialogTmSet.setParent(mYSelf);
			DialogTmSet.setLocation(mYSelf.getLocationOnScreen());
			ImageIcon icon = new ImageIcon(getClass().getClassLoader().getResource("images/starttime.png"));
			DialogTmSet.setIconImage(icon.getImage());		
		}
		return DialogTmSet;
	}
	private JFrame pArentFrame = null;
	private DynamicRangeView mYSelf = null;
	private JFrame getParentFrame(){
		if(pArentFrame==null){
			Component c0 = getParent();
			while(c0 != null){
				c0 = c0.getParent();
				if(c0 instanceof JFrame) break;
			}
			pArentFrame=(JFrame)c0;
		}
		return pArentFrame;
		//return (Frame)(this.getParent().getParent().getParent());
	}

	@Override
	public void setDispTimeStart(wintime wt) {
		DispStartTime = wt;
		onParameterChange();
		repaintAll();
	}
	public void doTimeSpanDialog(){
		String value = JOptionPane.showInputDialog(getParentFrame(), "Span [minutes]",(Object)DispTimeMin);	
		try{
			int min = Integer.valueOf(value);
			DispTimeMin=min;
			DirtyFlag=true;
			repaintAll();
			onParameterChange();
		}catch(NumberFormatException e){
			
		}
		
	}
	private void setAutoScaleTime(){
		//setTimeLatest();
		wintime oldtime = new wintime(DataBase.OldestTime);
		wintime newtime = new wintime(DataBase.LatestTime);
		int span = newtime.getValueS() - oldtime.getValueS();
		span /= 60;
		if(span < 2) span = 3;
		DispTimeMin = span;
		DispStartTime = new wintime(newtime.year(),newtime.month(),newtime.day(),newtime.hour(),newtime.min(),0,0);
		DispStartTime.dec_sec(DispTimeMin*60);
		DirtyFlag=true;
		onParameterChange();
	}
	public void autoScaleTime() {
		setAutoScaleTime();
		setData();
		DirtyFlag=true;
		repaintAll();
		
	}
	public void autoScaleAmplitude() {
		setAutoScaleAmplitude();
		DirtyFlag=true;
		repaintAll();
	}
	
	public void setTimeScaleZoom(int ratio,int ratio2,float xpos){
		wintime start = getDispStartTime();
		int     span  = getDispTimeMin();
		int startint = start.getValueS()/60;
		int center = startint + (int)((float)span*xpos);
		
		span = span*ratio/ratio2;
		int newstartint = center - span/2;
		int delta =  -newstartint + startint;
		start.dec_sec(delta*60);
		setDispStartTime(start);
		DirtyFlag=true;
		setDispTimeMin(span);
		onParameterChange();
	}
	public void setTimeScaleShift(boolean inc,int ratio){
		wintime start = getDispStartTime();
		int     span  = getDispTimeMin();
		int delta = span/ratio;
		if(delta==0) delta=1;
		if(inc) start.inc_sec(delta*60);
		else	start.dec_sec(delta*60);
		setDispStartTime(start);
		DirtyFlag=true;
		repaintAll();
		onParameterChange();
	}
	private void onParameterChange(){
		Parent.onStateChange(0);
	}
	private void onPropertyChange(){
		Parent.onStateChange(1);
	}

	public boolean isShowInfo() {
		return ShowInfo;
	}

	public void setShowInfo(boolean showInfo) {
		if(ShowInfo != showInfo){
			ShowInfo = showInfo;
			repaintAll();	
			onPropertyChange();
		}
	}

}
